amends ".../snippetTest.pkl"

import "pkl:reflect"
import ".../input-helper/api/reflect/BaseModule.pkl"
import ".../input-helper/api/reflect/testHelpers.pkl"

local mod = reflect.Module(BaseModule)
local modClass = mod.moduleClass
local modClassProps = modClass.properties

local personClass = mod.classes["Person"]
local myAnnClass = mod.classes["MyAnn"]

local myMapAlias = mod.typeAliases["MyMap"]

facts {
  ["Reflecting a module"] {
    mod.reflectee == BaseModule
    mod.annotations == testHelpers.MyAnn("module annotation")
    mod.docComment == "module doc comment"
    mod.uri.matches(Regex(#"file:///.*/input-helper/api/reflect/BaseModule.pkl"#))
    mod.supermodule == null
    !mod.isAmend
    mod.classes.keys == Set("Person", "MyAnn")
    mod.typeAliases.keys == Set("MyMap")
  }

  ["Reflecting a module class"] {
    modClass.reflectee == BaseModule.getClass()
    modClass.docComment == "module doc comment"
    modClass.annotations == testHelpers.MyAnn("module annotation")
    modClass.typeParameters == List()
    modClass.superclass.reflectee == Module
    modClass.supertype == reflect.DeclaredType(reflect.Class(Module))
  }

  ["Reflecting properties"] {
    modClassProps["any"].type == reflect.anyType
    modClassProps["noth"].type == reflect.nothingType
    modClassProps["unkn"].type == reflect.unknownType
    modClassProps["union"].type == reflect.UnionType(List(
        reflect.intType,
        reflect.listType.withTypeArgument(reflect.stringType),
        reflect.DeclaredType(reflect.Class(BaseModule.Person))))
    modClassProps["nullable"].type == reflect.DeclaredType(reflect.Class(BaseModule.Person)).nullable
    modClassProps["stringLiteral"].type == reflect.StringLiteralType("yes")
    modClassProps["constrained"].type == reflect.stringType
    modClassProps["aliased"].type ==
      reflect.DeclaredType(reflect.TypeAlias(BaseModule.MyMap))
        .withTypeArgument(reflect.DeclaredType(reflect.Class(BaseModule.Person)))
  }
  
  ["Reflecting a class"] {
    personClass.annotations == testHelpers.MyAnn("class annotation")
    personClass.docComment == "class doc comment"
    personClass.location.displayUri.contains("BaseModule.pkl")
    personClass.reflectee == BaseModule.Person
    personClass.typeParameters == List()
    personClass.superclass == reflect.Class(Typed)
    personClass.supertype == reflect.DeclaredType(personClass.superclass!!)
    personClass.isSubclassOf(reflect.Class(Any))
    personClass.isSubclassOf(reflect.Class(Typed))
    personClass.isSubclassOf(personClass)
    !personClass.isSubclassOf(reflect.Class(String))
    !personClass.isSubclassOf(myAnnClass)
  }
  
  ["Reflecting an annotation class"] {
    myAnnClass.annotations == List()
    myAnnClass.docComment == null
    myAnnClass.location.displayUri.contains("BaseModule.pkl")
    myAnnClass.reflectee == BaseModule.MyAnn
    myAnnClass.typeParameters == List()
    myAnnClass.superclass == reflect.Class(Annotation)
    myAnnClass.isSubclassOf(reflect.Class(Annotation))
    !reflect.Class(Annotation).isSubclassOf(myAnnClass)    
  }
  
  ["Reflecting an alias"] {
    myMapAlias.annotations == testHelpers.MyAnn("type alias annotation")
    myMapAlias.docComment == "type alias doc comment"
    myMapAlias.location.displayUri.contains("BaseModule.pkl")
    myMapAlias.reflectee == BaseModule.MyMap
    myMapAlias.typeParameters.first.toDynamic().toMap() == Map("name", "V", "variance", null)
    myMapAlias.referent == reflect.mapType.withTypeArguments(List(
        reflect.stringType,
        reflect.TypeVariable(myMapAlias.typeParameters[0])))    
  }
  
  ["Enclosing declarations"] {
    personClass.enclosingDeclaration == mod
    modClass.enclosingDeclaration == mod
    myMapAlias.enclosingDeclaration == mod    
  }
}

examples {
  ["Reflected module properties of unknown type metadata"] {
    modClassProps.keys
    testHelpers.property(modClassProps["int"], reflect.unknownType)
    testHelpers.property(modClassProps["float"], reflect.unknownType)
    testHelpers.property(modClassProps["string"], reflect.unknownType)
    testHelpers.property(modClassProps["boolean"], reflect.unknownType)
    testHelpers.property(modClassProps["duration"], reflect.unknownType)
    testHelpers.property(modClassProps["dataSize"], reflect.unknownType)
    testHelpers.property(modClassProps["pair"], reflect.unknownType)
    testHelpers.property(modClassProps["list"], reflect.unknownType)
    testHelpers.property(modClassProps["set"], reflect.unknownType)
    testHelpers.property(modClassProps["map"], reflect.unknownType)
    testHelpers.property(modClassProps["listing"], reflect.unknownType)
    testHelpers.property(modClassProps["mapping"], reflect.unknownType)
    testHelpers.property(modClassProps["dynamic"], reflect.unknownType)
    testHelpers.property(modClassProps["typed"], reflect.unknownType)
  }

  ["Reflected module properties of known type metadata"] {
    testHelpers.property(modClassProps["int2"], reflect.intType)
    testHelpers.property(modClassProps["float2"], reflect.floatType)
    testHelpers.property(modClassProps["string2"], reflect.stringType)
    testHelpers.property(modClassProps["boolean2"], reflect.booleanType)
    testHelpers.property(modClassProps["duration2"], reflect.durationType)
    testHelpers.property(modClassProps["dataSize2"], reflect.dataSizeType)
    testHelpers.property(modClassProps["pair2"], reflect.pairType.withTypeArguments(List(reflect.intType, reflect.stringType)))
    testHelpers.property(modClassProps["list2"], reflect.listType.withTypeArgument(reflect.intType))
    testHelpers.property(modClassProps["set2"], reflect.setType.withTypeArgument(reflect.intType))
    testHelpers.property(modClassProps["map2"], reflect.mapType.withTypeArguments(List(reflect.intType, reflect.stringType)))
    testHelpers.property(modClassProps["listing2"], reflect.listingType.withTypeArgument(reflect.intType))
    testHelpers.property(modClassProps["mapping2"], reflect.mappingType.withTypeArguments(List(reflect.intType, reflect.stringType)))
    testHelpers.property(modClassProps["dynamic2"], reflect.dynamicType)
    testHelpers.property(modClassProps["typed2"], reflect.DeclaredType(reflect.Class(BaseModule.Person)))
  }

  ["Reflected module methods metadata"] {
    local modClassMethods = modClass.methods
    modClassMethods.keys

    testHelpers.method(
      modClassMethods["sing"],
      List(reflect.unknownType, reflect.unknownType),
      reflect.unknownType)

    testHelpers.method(
      modClassMethods["sing2"],
      List(
        reflect.DeclaredType(reflect.Class(BaseModule.Person)),
        reflect.listType.withTypeArgument(reflect.stringType)),
      reflect.stringType)
  }
  
  ["Reflected class properties metadata"] {
    local personClassProperties = personClass.properties
    personClassProperties.keys
    testHelpers.property(personClassProperties["name"], reflect.stringType)
    testHelpers.property(personClassProperties["age"], reflect.intType)
    modClassProps["hiddenProp"].modifiers
    modClassProps["constProp"].modifiers
    modClassProps["fixedProp"].modifiers
  }

  ["Reflected class methods metadata"] {
    local personClassMethods = personClass.methods
    personClassMethods.keys
    testHelpers.method(
        personClassMethods["sing"],
        List(reflect.listType.withTypeArgument(reflect.stringType)),
        reflect.stringType)
  }

  ["Reflected annotation class properties metadata"] {
    testHelpers.property(myAnnClass.properties["text"], reflect.stringType)
  }
}
